package com.hospital.workstation.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.ObjectUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.hospital.common.core.domain.R;
import com.hospital.common.exception.ServiceException;
import com.hospital.common.utils.StringUtils;
import com.hospital.common.core.page.TableDataInfo;
import com.hospital.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hospital.workstation.domain.HisMedicalOrders;
import com.hospital.workstation.domain.bo.TcmExportBo;
import com.hospital.workstation.domain.bo.TcmRecommendPrescriptionBo;
import com.hospital.workstation.domain.vo.HisMedicalOrdersVo;
import com.hospital.workstation.mapper.HisMedicalOrdersMapper;
import com.hospital.workstation.utils.PoiTlUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.map.ListOrderedMap;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import com.hospital.workstation.domain.bo.TcmPrescriptionBo;
import com.hospital.workstation.domain.vo.TcmPrescriptionVo;
import com.hospital.workstation.domain.TcmPrescription;
import com.hospital.workstation.mapper.TcmPrescriptionMapper;
import com.hospital.workstation.service.ITcmPrescriptionService;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.FileAlreadyExistsException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 中医证型方剂信息Service业务层处理
 *
 * @author whb
 * @date 2023-06-20
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class TcmPrescriptionServiceImpl implements ITcmPrescriptionService {

    private final TcmPrescriptionMapper baseMapper;
    private final HisMedicalOrdersMapper medicalOrdersMapper;

    /**
     * 查询中医证型方剂信息
     */
    @Override
    public TcmPrescriptionVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询中医证型方剂信息列表
     */
    @Override
    public TableDataInfo<TcmPrescriptionVo> queryPageList(TcmPrescriptionBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TcmPrescription> lqw = buildQueryWrapper(bo);
        Page<TcmPrescriptionVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询中医证型方剂信息列表
     */
    @Override
    public List<TcmPrescriptionVo> queryList(TcmPrescriptionBo bo) {
        LambdaQueryWrapper<TcmPrescription> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<TcmPrescription> buildQueryWrapper(TcmPrescriptionBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<TcmPrescription> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getIllnessName()), TcmPrescription::getIllnessName, bo.getIllnessName())
            .like(StringUtils.isNotBlank(bo.getSymptomName()), TcmPrescription::getSymptomName, bo.getSymptomName())
            .orderByAsc(TcmPrescription::getIllnessNo);
        return lqw;
    }

    /**
     * 新增中医证型方剂信息
     */
    @Override
    public Boolean insertByBo(TcmPrescriptionBo bo) {
        TcmPrescription add = BeanUtil.toBean(bo, TcmPrescription.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改中医证型方剂信息
     */
    @Override
    public Boolean updateByBo(TcmPrescriptionBo bo) {
        TcmPrescription update = BeanUtil.toBean(bo, TcmPrescription.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(TcmPrescription entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除中医证型方剂信息
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 导入中医证型方剂数据
     *
     * @param tcmPrescriptionVoList
     * @return
     */
    @Override
    public R<Void> addBatchTcmPrescription(List<TcmPrescriptionVo> tcmPrescriptionVoList) {
        return baseMapper.insertOrUpdateBatch(BeanUtil.copyToList(tcmPrescriptionVoList, TcmPrescription.class)) ? R.ok("导入成功") : R.fail("导入失败");
    }

    /**
     * 查询下拉列表数据
     *
     * @param bo    证型方剂业务对象
     * @param level 病名和证型下拉分级标识（1 病名 2证型）
     * @return
     */
    @Override
    public R<List<String>> getTcmSelectList(TcmPrescriptionBo bo, String level) {
        // 查询中医证型方剂数据列表
        List<TcmPrescription> tcmPrescriptionList = baseMapper.selectList(buildQueryWrapper(bo));
        // 封装返回病名或证型下拉数据
        try {
            if (StringUtils.equals("1", level)) { // 病名
                List<String> illNessNameList = tcmPrescriptionList.stream().map(TcmPrescription::getIllnessName).collect(Collectors.toList());
                illNessNameList = illNessNameList.stream().distinct().collect(Collectors.toList());
                return R.ok("查询成功", illNessNameList);
            } else if (StringUtils.equals("2", level)) { // 证型
                List<String> symptomNameList = tcmPrescriptionList.stream().map(TcmPrescription::getSymptomName).collect(Collectors.toList());
                symptomNameList = symptomNameList.stream().distinct().collect(Collectors.toList());
                return R.ok("查询成功", symptomNameList);
            } else {
                return R.fail("病名和证型下拉分级标识错误");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return R.fail("查询失败");
        }
    }

    /**
     * 查询中药饮片
     *
     * @param medicalRecordNo 流水号
     * @return
     */
    @Override
    public R<Map<String, List<HisMedicalOrdersVo>>> getTraditionalMedicalOrdersByMedicalRecordNo(Integer medicalRecordNo) {
        try {
            Map<String, List<HisMedicalOrdersVo>> returnMap = new ListOrderedMap<>();
            List<HisMedicalOrdersVo> medicalOrdersVoList = medicalOrdersMapper.selectVoList(new LambdaQueryWrapper<HisMedicalOrders>()
                .eq(HisMedicalOrders::getMedicalRecordNo, medicalRecordNo)
                .eq(HisMedicalOrders::getOrderCategory, "中药饮片")
                .select(HisMedicalOrders::getMedicalRecordNo, HisMedicalOrders::getOrderItemId,
                    HisMedicalOrders::getOrderGroupId, HisMedicalOrders::getOrderItemDesc,
                    HisMedicalOrders::getDoseQty, HisMedicalOrders::getDoseUnitCode
                    , HisMedicalOrders::getInstrDesc, HisMedicalOrders::getFreqCode, HisMedicalOrders::getDurationDesc));
            List<String> groupIdList = medicalOrdersVoList.stream().filter(hisMedicalOrdersVo -> StringUtils.isBlank(hisMedicalOrdersVo.getOrderGroupId()))
                .map(hisMedicalOrdersVo -> hisMedicalOrdersVo.getOrderItemId())
                .collect(Collectors.toList());
            groupIdList.stream().forEach(groupId -> {
                List<HisMedicalOrdersVo> list = new ArrayList<>();
                medicalOrdersVoList.stream().forEach(hisMedicalOrdersVo -> {
                    if (StringUtils.equals(hisMedicalOrdersVo.getOrderItemId(), groupId)
                        || StringUtils.equals(hisMedicalOrdersVo.getOrderGroupId(), groupId)) {
                        list.add(hisMedicalOrdersVo);
                    }
                });
                returnMap.put(groupId, list);
            });
            return R.ok("查询成功", returnMap);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return R.fail("查询失败");
        }
    }

    /**
     * 审核
     *
     * @param bo
     * @return
     */
    @Override
    public R<List<TcmPrescriptionVo>> audit(TcmPrescriptionBo bo) {
        try {
            List<TcmPrescriptionVo> auditVoList = new ArrayList<>();
            // 根据病和症查询推荐处方
            List<TcmPrescriptionVo> tcmPrescriptionVoList = baseMapper.selectVoList(buildQueryWrapper(bo));
            // 遍历推荐处方
            tcmPrescriptionVoList.stream().forEach(tcmPrescriptionVo -> {
                // 创建方剂加减列表
                List<String> addAndReduceList = new ArrayList<>();
                String addAndSubtract = tcmPrescriptionVo.getAddAndSubtract();
                if (StringUtils.isNotBlank(addAndSubtract)) {
                    String[] split = addAndSubtract.split(";");
                    addAndReduceList = Arrays.asList(split);
                }
                tcmPrescriptionVo.setAddAndSubtractList(addAndReduceList);
                String compositionAndMeasurement = tcmPrescriptionVo.getCompositionAndMeasurement();
                if (StringUtils.isNotBlank(compositionAndMeasurement)) {
                    // 一致率
                    String[] split = compositionAndMeasurement.split("，");
                    List<String> traditionalNameList = bo.getTraditionalNameList();
                    if (ObjectUtil.isNotNull(traditionalNameList) && traditionalNameList.size() > 0) {
                        BigDecimal haveNum = new BigDecimal(0); // 推荐处方包含医嘱用药数目
                        Map redMap = new ListOrderedMap(); // 推荐处方Map（包含医嘱用药设置标红标志）
                        for (int i = 0; i < split.length; i++) { // 循环推荐处方药名数组
                            for (int j = 0; j < traditionalNameList.size(); j++) { // 循环医嘱重要处方药名列表
                                if (split[i].contains(traditionalNameList.get(j))) { // 推荐处方包含医嘱用药
                                    haveNum = haveNum.add(new BigDecimal(1));
                                    redMap.put(split[i], "1");
                                    break;
                                } else {
                                    redMap.put(split[i], "0");
                                }
                            }
                        }
                        BigDecimal rate = new BigDecimal(0);
                        if (haveNum.compareTo(BigDecimal.ZERO) != 0) {
                            rate = haveNum.divide(new BigDecimal(split.length), 4, RoundingMode.HALF_UP);
                        }
                        tcmPrescriptionVo.setConcordanceRate(String.valueOf(rate.multiply(new BigDecimal(100)).setScale(2, 4)) + "%");
                        tcmPrescriptionVo.setRecommendOfRedMap(redMap);
                    }
                    // 推荐处方列表（包含医嘱用药的标红）

                }
                auditVoList.add(tcmPrescriptionVo);
            });
            return R.ok("查询成功", auditVoList);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return R.fail("查询失败");
        }
    }

    /**
     * 导出处方审核结果
     *
     * @param response
     * @param exportBo
     */
    @Override
    public void export(HttpServletResponse response, TcmExportBo exportBo) {
        try {
            // 处方审核结果数据集合
            Map<String, Object> datas = new HashMap<>();
            // 患者基本信息
            // 病案号
            datas.put("medicareNo", exportBo.getMedicareNo());
            // 患者姓名
            datas.put("patientName", exportBo.getPatientName());
            // 患者性别
            datas.put("patientSex", exportBo.getPatientSex());
            // 患者年龄
            datas.put("patientAge", exportBo.getPatientAge());
            // 住院医师
            datas.put("doctorName", exportBo.getDoctorName());
            // 选择的病名
            datas.put("selectIllnessName", exportBo.getSelectIllnessName());
            // 中药饮片处方
            datas.put("traditionalPrescription", exportBo.getTraditionalPrescription());
            // 选择的证型
            datas.put("selectIllnessSymptom", exportBo.getSelectIllnessSymptom());
            // 创建循环策略和配置
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            Configure config = Configure.builder()
                .bind("tcmDiagInfoBoList", policy)
                .bind("tcmRecommendPrescriptionBoList", policy)
                .build();
            // 病症列表保存到渲染的Map中
            datas.put("tcmDiagInfoBoList", exportBo.getTcmDiagInfoBoList());
            // 推荐处方列表
            List<TcmRecommendPrescriptionBo> tcmRecommendPrescriptionBoList = exportBo.getTcmRecommendPrescriptionBoList();
            // 循环处理一致性药物
            tcmRecommendPrescriptionBoList.stream().forEach(tcmRecommendPrescriptionBo -> {
                // 获取一致性标红map
                Map<String, String> recommendOfRedMap = tcmRecommendPrescriptionBo.getRecommendOfRedMap();
                // 获取key集合
                Object[] keySetArr = recommendOfRedMap.keySet().toArray();
                Arrays.sort(keySetArr);
                List<Object> compositionAndMeasurementList = Arrays.asList(keySetArr);
                // 封装方剂组成与剂量
                tcmRecommendPrescriptionBo.setCompositionAndMeasurement(StringUtils.join(compositionAndMeasurementList, " "));
                // 过滤出相同的中药
                Map<String, String> haveMedicineMap = recommendOfRedMap.entrySet().stream().filter(map -> "1".equals(map.getValue())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
                Object[] haveKeySetArr = haveMedicineMap.keySet().toArray();
                Arrays.sort(haveKeySetArr);
                List<Object> haveMedicineList = Arrays.asList(haveKeySetArr);
                // 封装一致的中药
                tcmRecommendPrescriptionBo.setConcordanceMedicine(StringUtils.join(haveMedicineList, " "));
            });
            // 推荐处方列表保存到渲染的Map中
            datas.put("tcmRecommendPrescriptionBoList", tcmRecommendPrescriptionBoList);
            // 导出
            ResourceLoader resourceLoader = new DefaultResourceLoader();
            Resource resource = resourceLoader.getResource("classpath:template/tcm_audit_result.docx");
            InputStream inputStream = resource.getInputStream();
            XWPFTemplate template = XWPFTemplate.compile(inputStream, config).render(datas);
            PoiTlUtils.createOutPusStream(response, template, exportBo.getMedicareNo() + "-" + exportBo.getPatientName() + "-审核结果");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new ServiceException("导出失败");
        }
    }
}
